#include "MpegFile.h"
#include <stdio.h>

#define SYNC			0xFFF00000L
#define VERSION			0x00080000L
#define LAYER_MASK		0x00060000L
#define LAYER_FACT		0x00020000L
#define ERRORPROTECT	0x00010000L
#define BITRATE_MASK	0x0000F000L
#define BITRATE_FACT	0x00001000L
#define SAMPLERATE_MASK	0x00000C00L
#define SAMPLERATE_FACT	0x00000400L
#define PADDING			0x00000200L
#define EXTENSION		0x00000100L
#define CHANNELMOD_MASK	0x000000C0L
#define CHANNELMOD_FACT	0x00000040L
#define MODEEXTENS_MASK 0x00000030L
#define MODEEXTENS_FACT 0x00000010L
#define COPYRIGHT		0x00000008L
#define ORIGINAL		0x00000004L
#define EMPHASIS_MASK	0x00000003L
#define EMPHASIS_FACT	0x00000001L

#define TYPECOMP_MASK (SYNC | VERSION | LAYER_MASK | ERRORPROTECT | BITRATE_MASK \
                       | SAMPLERATE_MASK | EXTENSION | CHANNELMOD_MASK | COPYRIGHT \
                       | ORIGINAL | EMPHASIS_MASK)

CMpegHeader::CMpegHeader()
	{
	HeaderData = 0;
	LastHeadFilePos = -1L;
	}

CMpegHeader::CMpegHeader(const BYTE *b)
	{
	Build(b);
	LastHeadFilePos = -1L;
	}

CMpegHeader::~CMpegHeader()
	{
	}

void CMpegHeader::Build(const BYTE *b)
	{
	HeaderData = (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3];
	}

bool CMpegHeader::Load(FILE *fp)
	{
	BYTE b[4];

	LastHeadFilePos = ftell(fp);
	if (fread(b, 1, 4, fp) != 4)
		return FALSE;
	Build(b);
	return TRUE;
	}


bool CMpegHeader::Load(CFile &file)
	{
	BYTE b[4];

	LastHeadFilePos = file.GetPosition();
	if (file.Read(b, 4) != 4)
		return FALSE;
	Build(b);
	return TRUE;
	}


bool CMpegHeader::LoadNext(FILE *fp)
	{
	BYTE b[4];
	CMpegHeader TestHeader;
	int i, j;

	if (LastHeadFilePos >= 0 && ftell(fp) < LastHeadFilePos + StdBlockSize() - 5) 
		fseek(fp, LastHeadFilePos + StdBlockSize() - 5, SEEK_SET);

	b[0] = 0;
	while (TRUE)
		{
		while (b[0] != 0xff)
			if (fread(b, 1, 1, fp) != 1)
				return FALSE;
		if (fread(b+1, 1, 3, fp) != 3)
			return FALSE;
		while (b[0] == 0xff)
			{
			TestHeader.Build(b);
			if (TestHeader.SyncOk() 
				&& (LastHeadFilePos < 0 || TypeComp(TestHeader)))
				{
				HeaderData = TestHeader.HeaderData;
				LastHeadFilePos = ftell(fp) - 4;
				return TRUE;
				}
			b[0] = 0;
			for (i = 1 ; i < 4 && b[i] != 0xff ; i++)
				;
			if (i < 4)
				{
				for (j = 0 ; i < 4 ; j++, i++)
					b[j] = b[i];
				if (fread(b+j, 1, 4 - j, fp) != unsigned(4 - j))
					return FALSE;
				}
			}
		}
	}


bool CMpegHeader::LoadNext(CFile &file)
	{
	BYTE b[4];
	CMpegHeader TestHeader;
	int i, j;

	if (LastHeadFilePos >= 0 
		&& long(file.GetPosition()) >= LastHeadFilePos
		&& long(file.GetPosition()) < LastHeadFilePos + StdBlockSize() - 5) 
		file.Seek(LastHeadFilePos + StdBlockSize() - 5, CFile::begin);

	b[0] = 0;
	while (TRUE)
		{
		while (b[0] != 0xff)
			if (file.Read(b, 1) != 1)
				return FALSE;
		if (file.Read(b+1, 3) != 3)
			return FALSE;
		while (b[0] == 0xff)
			{
			TestHeader.Build(b);
			if (TestHeader.SyncOk() 
				&& (LastHeadFilePos < 0 || TypeComp(TestHeader)))
				{
				HeaderData = TestHeader.HeaderData;
				LastHeadFilePos = file.GetPosition() - 4;
				return TRUE;
				}
			b[0] = 0;
			for (i = 1 ; i < 4 && b[i] != 0xff ; i++)
				;
			if (i < 4)
				{
				for (j = 0 ; i < 4 ; j++, i++)
					b[j] = b[i];
				if (file.Read(b+j, 4 - j) != unsigned(4 - j))
					return FALSE;
				}
			}
		}
	}


const bool CMpegHeader::SyncOk()
	{
	return (HeaderData & SYNC) == SYNC;
	}

const int CMpegHeader::GetVersion()
	{
	return (HeaderData & VERSION) ? 1 : 2;
	}

const int CMpegHeader::GetLayer()
	{
	switch ((HeaderData & LAYER_MASK) / LAYER_FACT)
		{	
		case 0: return 1;
		case 1: return 3; 
		case 2: return 2; 
		default: return -1;
		}
	}

const bool CMpegHeader::GetErrorProtection()
	{
	return (HeaderData & ERRORPROTECT) == 0;
	}

const long CMpegHeader::GetBitRate()
	{
	static int Data[2][4][16] = {
	      /* b=0,  1,  2,  3,  4,  5,  6,  7,   8,   9,   10,  11,  12,  13,  14, 15 */
/* v=0 l=0 */ -1, 16, 20, 24, 32, 64, 80,  56,  64, 128, 160, 112, 128, 256, 320, -1,
/* v=0 l=1 */ -1, 16, 20, 24, 32, 64, 80,  56,  64, 128, 160, 112, 128, 256, 320, -1,
/* v=0 l=2 */ -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
/* v=0 l=3 */ -1, -1, -1, -1, -1, -1, -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1,
/* v=1 l=0 */ -1, 16, 20, 24, 32, 64, 80,  56,  64, 128, 160, 112, 128, 256, 320, -1,
/* v=1 l=1 */ -1, 32, 40, 48, 56, 64, 80,  96, 112, 128, 160, 192, 224, 256, 320, -1,
/* v=1 l=2 */ -1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
/* v=1 l=3 */ -1, -1, -1, -1, -1, -1, -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1
		};

	return Data[(HeaderData & VERSION) ? 1 : 0][(HeaderData & LAYER_MASK) / LAYER_FACT][(HeaderData & BITRATE_MASK) / BITRATE_FACT] * 1000;
	}

const long CMpegHeader::GetSampleRate()
	{
	long res;

	switch ((HeaderData & SAMPLERATE_MASK) / SAMPLERATE_FACT)
		{	
		case 0: res = 44100; break;
		case 1: res = 48000; break;
		case 2: res = 32000; break;
		default: return -1;
		}

	if (((HeaderData & VERSION) == 0) && ((HeaderData & LAYER_MASK) == LAYER_FACT))
		return res / 2;
	else
		return res;
	}

const bool CMpegHeader::GetPadding()
	{
	return (HeaderData & PADDING) != 0;
	}

const bool CMpegHeader::GetPrivate()
	{
	return (HeaderData & EXTENSION) != 0;
	}

const int CMpegHeader::GetChannelMode()
	{
	switch ((HeaderData & CHANNELMOD_MASK) / CHANNELMOD_FACT)
		{
		case 0: return Stereo;
		case 1: return JointStereo;
		case 2: return DualChannel;
		case 3: return Mono;
		default: return -1;
		}
	}

const long CMpegHeader::GetJointBoundFreq()
	{
	if (GetChannelMode() == JointStereo)
		switch ((HeaderData & MODEEXTENS_MASK) / MODEEXTENS_FACT)
			{
			case 0: return (GetLayer() < 3) ? 4 : 0;
			case 1: return (GetLayer() < 3) ? 8 : 4;
			case 2: return (GetLayer() < 3) ? 12 : 8;
			case 3: return 16;
			default: return -1;
			}
	else
		return -1;
	}

const bool CMpegHeader::GetCopyright()
	{
	return (HeaderData & COPYRIGHT) != 0;
	}

const bool CMpegHeader::GetOriginal()
	{
	return (HeaderData & ORIGINAL) != 0;
	}

const int CMpegHeader::GetEmphasis()
	{
	switch ((HeaderData & EMPHASIS_MASK) / EMPHASIS_FACT)
		{
		case 0: return EmphasisNo;
		case 1: return Emphasis50_15;
		case 3: return EmphasisCCITTj17;
		default: return -1;
		}
	}

const bool CMpegHeader::TypeComp(const CMpegHeader &h2)
	{
	return (HeaderData & TYPECOMP_MASK) == (h2.HeaderData & TYPECOMP_MASK);
	}


const long CMpegHeader::StdBlockSize()
	{
	if (GetVersion() == 1)
		return 144L * GetBitRate() / GetSampleRate();
	else
		return 72L * GetBitRate() / GetSampleRate();
	}


const long CMpegHeader::GetHeaderFilePos()
	{
	return LastHeadFilePos;
	}


const double CMpegHeader::GetLength(long Filelen)
	{
	double res = double(Filelen) * 8.0 / GetBitRate();

	return (res <= 0.0) ? -1.0 : res;
	}

